﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;
using System.ComponentModel;

namespace Mdv.wssAddins.wss
{
    [PropertyTab(typeof(XMLPropertyTab), PropertyTabScope.Component)]
    public abstract class Model
    {
        private List<Model> _children;
        [Browsable(false)]
        public List<Model> Children
        {
            get { return _children; }
            set { _children = value; }
        }
        private Model _parent;
        [Browsable(false)]
        public Model Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }
        private List<Action> _actions;
        [Browsable(false)]
        public List<Action> Actions
        {
            get { return _actions; }
            set { _actions = value; }
        }
        private Dictionary<string, string> _map = new Dictionary<string, string>();
        private XmlNode _xmlDefinition = null;
        [Browsable(false)]
        [EditorAttribute(typeof(XmlEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public XmlNode XmlDefinition
        {
            get { return _xmlDefinition; }
            set { _xmlDefinition = value; }
        }
        [Browsable(false)]
        public Dictionary<string, string> PropertiesToXmlMap
        {
            get { return _map; }
        }
        public void LoadProperties(XmlNode node)
        {
            foreach (string propertyName in PropertiesToXmlMap.Keys)
            {
                if (null != node.Attributes[PropertiesToXmlMap[propertyName]])
                {
                    string value = node.Attributes[PropertiesToXmlMap[propertyName]].Value;
                    if(!String.IsNullOrEmpty(value))
                    {
                        PropertyInfo pi = this.GetType().GetProperty(propertyName);
                        if (pi.PropertyType == typeof(Guid))
                            pi.SetValue(this, new Guid(value), null);
                        if (pi.PropertyType == typeof(string))
                            pi.SetValue(this, value, null);
                        if (pi.PropertyType == typeof(int))
                            pi.SetValue(this, int.Parse(value), null);
                        if (pi.PropertyType == typeof(int?))
                        {
                            int t;
                            if(int.TryParse(value, out t))
                                pi.SetValue(this, t, null);
                        }
                        if (pi.PropertyType == typeof(bool))
                            pi.SetValue(this, bool.Parse(value), null);
                        if (pi.PropertyType == typeof(bool?))
                        {
                            bool t;
                            if (bool.TryParse(value, out t))
                                pi.SetValue(this, t, null);
                        }
                        if (pi.PropertyType == typeof(wssDir?))
                            pi.SetValue(this, (wssDir)Enum.Parse(typeof(wssDir), value, true), null);
                        if (pi.PropertyType == typeof(wssFieldAggregation?))
                            pi.SetValue(this, (wssFieldAggregation)Enum.Parse(typeof(wssFieldAggregation), value, true), null);
                        if (pi.PropertyType == typeof(wssFieldBaseType?))
                            pi.SetValue(this, (wssFieldBaseType)Enum.Parse(typeof(wssFieldBaseType), value, true), null);
                        if (pi.PropertyType == typeof(wssIMEMode?))
                            pi.SetValue(this, (wssIMEMode)Enum.Parse(typeof(wssIMEMode), value, true), null);
                        if (pi.PropertyType == typeof(wssFieldJoinType?))
                            pi.SetValue(this, (wssFieldJoinType)Enum.Parse(typeof(wssFieldJoinType), value, true), null);
                        if (pi.PropertyType == typeof(wssNegativeFormat?))
                            pi.SetValue(this, (wssNegativeFormat)Enum.Parse(typeof(wssNegativeFormat), value, true), null);
                        if (pi.PropertyType == typeof(wssStorageTZ?))
                            pi.SetValue(this, (wssStorageTZ)Enum.Parse(typeof(wssStorageTZ), value, true), null);
                        if (pi.PropertyType == typeof(wssFieldFormat?))
                            pi.SetValue(this, (wssFieldFormat)Enum.Parse(typeof(wssFieldFormat), value, true), null);
                        if (pi.PropertyType == typeof(wssCalType?))
                            pi.SetValue(this, (wssCalType)Enum.Parse(typeof(wssCalType), value, true), null);
                        if (pi.PropertyType == typeof(wssUserSelectionMode?))
                            pi.SetValue(this, (wssUserSelectionMode)Enum.Parse(typeof(wssUserSelectionMode), value, true), null);
                        if (pi.PropertyType == typeof(wssFormType?))
                            pi.SetValue(this, (wssFormType)Enum.Parse(typeof(wssFormType), value, true), null);
                        if (pi.PropertyType == typeof(wssModerationType?))
                            pi.SetValue(this, (wssModerationType)Enum.Parse(typeof(wssModerationType), value, true), null);
                        if (pi.PropertyType == typeof(wssPageType?))
                            pi.SetValue(this, (wssPageType)Enum.Parse(typeof(wssPageType), value, true), null);
                        if (pi.PropertyType == typeof(wssViewScope?))
                            pi.SetValue(this, (wssViewScope)Enum.Parse(typeof(wssViewScope), value, true), null);
                        if (pi.PropertyType == typeof(wssTargetType?))
                            pi.SetValue(this, (wssTargetType)Enum.Parse(typeof(wssTargetType), value, true), null);
                        if (pi.PropertyType == typeof(wssViewType?))
                            pi.SetValue(this, (wssViewType)Enum.Parse(typeof(wssViewType), value, true), null);
                    }
                }
            }
        }
        public void Save()
        {
            Save(XmlDefinition);
        }
        public void Save(XmlNode node)
        {
            if (null != node)
            {
                foreach (string propertyName in PropertiesToXmlMap.Keys)
                {
                    PropertyInfo pi = this.GetType().GetProperty(propertyName);
                    if (null != node.Attributes[PropertiesToXmlMap[propertyName]])
                    {
                        node.Attributes[PropertiesToXmlMap[propertyName]].Value = pi.GetValue(this, null).ToString();
                    }
                    else
                    {
                        XmlAttribute xa = node.OwnerDocument.CreateAttribute(PropertiesToXmlMap[propertyName]);
                        object propValue = pi.GetValue(this, null);
                        if (null != propValue)
                        {
                            xa.Value = propValue.ToString();
                            node.Attributes.Append(xa);
                        }
                    }
                }
                if(null != Children)
                    foreach (Model m in Children)
                        m.Save();
            }
        }
        public void Load(XmlNode node)
        {
            _xmlDefinition = node;
            ConstructMap();
            LoadProperties(node);
            if (node.HasChildNodes)
            {
                Children = new List<Model>();
                LoadChildren(node);
                foreach (Model child in Children)
                    child.Parent = this;
            }
        }
        public virtual void ConstructMap(){}
        public virtual void LoadChildren(XmlNode node){}
        protected void NotifyModelChanged(ModelEventArgs e)
        {
            if (null != ModelChanged)
                ModelChanged(this, e);
        }
        public event System.EventHandler<ModelEventArgs> ModelChanged;
        #region Helper functions
        protected void NodeAdded(Model newNode, String newNodeXmlType)
        {
            newNode.Parent = this;
            newNode.XmlDefinition = XmlDefinition.OwnerDocument.CreateNode(XmlNodeType.Element, newNodeXmlType, XmlDefinition.NamespaceURI);
            newNode.ConstructMap();
            XmlDefinition.AppendChild(newNode.XmlDefinition);
            if (null == Children) Children = new List<Model>();
            Children.Add(newNode);
            ModelEventArgs e = new ModelEventArgs();
            e.ActionType = ActionType.Added;
            e.AffectedNode = newNode;
            NotifyModelChanged(e);
        }
        protected void NodeRenamed()
        {
            ModelEventArgs e = new ModelEventArgs();
            e.ActionType = ActionType.Renamed;
            NotifyModelChanged(e);
        }
        protected void NodeDeleted(String uniqueAttribute)
        {
            Model parent = this.Parent;
            if (null != Parent)
            {
                foreach (XmlNode xn in parent.XmlDefinition.ChildNodes)
                    if (null != xn.Attributes[uniqueAttribute] && 
                        null != XmlDefinition.Attributes[uniqueAttribute] && 
                        0 == String.Compare(xn.Attributes[uniqueAttribute].Value, XmlDefinition.Attributes[uniqueAttribute].Value, true))
                    {
                        parent.XmlDefinition.RemoveChild(xn);
                        break;
                    }
                parent.Children.Remove(this);
                ModelEventArgs e = new ModelEventArgs();
                e.ActionType = ActionType.Deleted;
                e.AffectedNode = this;
                NotifyModelChanged(e);
            }
        }
        #endregion
    }
    public enum ActionType { Added, Deleted, Renamed }
    public class ModelEventArgs : EventArgs
    {
        public ActionType ActionType;
        public Model AffectedNode;
    }
    public delegate void ActionEventHandler(ModelEventArgs e);
    public delegate void ModelChangedHandler(ModelEventArgs e);
    public class Action
    {
        public Action(String name, ActionEventHandler actionHandler)
        {
            _name = name;
            _actionHandler = actionHandler;
        }
        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        private ActionEventHandler _actionHandler;
        public void ActionHandler(object sender, EventArgs e)
        {
            ModelEventArgs me = new ModelEventArgs();
            if(null != _actionHandler)
                _actionHandler(me);
        }
    }
}